home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Networking / OT PAPServerSample / PAPServerSample.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  50.9 KB  |  1,941 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        PAPServerSample.c
  3.  
  4.     Contains:    This sample demonstrates the implementation of a PAP server using Open Transport.
  5.     
  6.                 Issues:
  7.                 1.     PostScript Query responses are default only
  8.                     To respond to the LaserWriter client, the PostScript query handler code is 
  9.                     designed to return the default responses.  A real PAP spooler would have to
  10.                     respond more appropriately for the printer that it is supporting.  This could mean
  11.                     delaying the response back to the client until the spooler can actually
  12.                     pass the query to the printer and get a real response.  The code for detecting
  13.                     a PostScript query is not very robust.  The purpose of this sample was not to
  14.                     demonstrate PostScript support, but to packet flow via OT.
  15.     
  16.                 2.     If the received data is not part of a PostScript query, the spooler sample
  17.                     assumes that all of the incoming data is part of the same job.  There is no check
  18.                     for the PostScript EOF indicator to distinguish different spooler jobs.  All of the 
  19.                     incoming data is saved to a temporary disk file at the root of the boot hard drive.
  20.                     The code does not try to preflight the amount of storage present, nor protect  
  21.                     some minimum hard drive storage in case the hard drive is close to full.
  22.     
  23.                 3.    This sample requires OT 1.1.2 or greater.
  24.     
  25.                 4.     This sample support multiple handoff endpoints.  The constant kMaxHandoffEPs
  26.                     defines the maximum number of handoff endpoints that this sample supports.  I have
  27.                     tested that the server supports 2 endpoint connections at the same time, but have not
  28.                     gone into testing more simultaneous connections.      
  29.  
  30.     Written by: Rich Kubota    
  31.  
  32.     Copyright:    Copyright © 1999 by Apple Computer, Inc., All Rights Reserved.
  33.  
  34.                 You may incorporate this Apple sample source code into your program(s) without
  35.                 restriction. This Apple sample source code has been provided "AS IS" and the
  36.                 responsibility for its operation is yours. You are not permitted to redistribute
  37.                 this Apple sample source code as "Apple sample source code" after having made
  38.                 changes. If you're going to re-distribute the source, we require that you make
  39.                 it clear in the source that the code was descended from Apple sample source
  40.                 code, but that you've made changes.
  41.  
  42.     Change History (most recent first):
  43.                 7/22/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  44.                 
  45.  
  46. */
  47. #define  HANDOFF_EP            1    // set to to to indicate use of handoff endpoint
  48. #include <GestaltEqu.h>
  49. #include <TextUtils.h>
  50. #include <DiskInit.h>
  51. #include <Types.h>
  52. #include <Errors.h>
  53. #include <String.h>
  54. #include <ToolUtils.h>
  55. #include "StringUtils.h"
  56. #include <AppleTalk.h>
  57. #include "OpenTransport.h"
  58. #include "OpenTptAppleTalk.h"
  59. #include "ATalkSampleUtils.h"
  60. #include "PAPServerUtilities.h"
  61. #include "PAPServerSample.h"
  62. #include "PAPPostScriptStuff.h"
  63. extern  OSStatus OTSetMemoryLimits(size_t growSize, size_t maxSize);
  64.  
  65. /********************/
  66. /*     globals            */
  67. /********************/
  68. DDPAddress        gAddr;
  69. MyEndpointRef    gEp;
  70.  
  71. #if HANDOFF_EP
  72. MyEndpointRef    gHandoffEp[kMaxHandoffEPs];
  73. #endif
  74.  
  75. OTLIFO*            gFreeQ;            // note that an OTLIFO structure must be on a 4-byte boundary
  76.                                 // instead of trying to force it to be on a 4-byte boundary in
  77.                                 // global memory, we use a pointer, then allocate memory
  78.                                 // which will force the global to be on a 4-byte boundary.
  79. UInt32            gFlags    = 0;
  80. UInt32            gBufsAvail;
  81. PacketPtr        gTempPackPtr;
  82. short            gFRefNumToClose = 0;
  83. Boolean            gDone    = false;
  84. Str255            gIdleStr;
  85. Str255            gBusyStr;
  86. Str255            gServerNBPNameStr;
  87. UInt32            gOptionCompleted; // used to flag completion of Option Management calls
  88. extern char        gEOFStr[8];
  89. extern char        gBeginPSStr[8];
  90. extern char        qBeginQueryStr[8];
  91. extern char        gEndStr[8];
  92. extern char        gQueryStr[8];
  93.  
  94. OSType            gOTVersionSelector    = 'otvr';
  95. UInt32            gOTVersion;
  96.  
  97.  
  98. /*******************************************************************************
  99. ** Prototypes
  100. ********************************************************************************/
  101.  
  102. pascal    void    EventHandler(void*, OTEventCode, OTResult, void*);
  103. static void     EnterListenAccept(MyEndpointRef *myServerEp);
  104. void            DoListenAccept(MyEndpointRef *myServerEp);
  105. Boolean         ReceiveOnePacket(MyEndpointRef *myEp, PacketPtr packetPtr);
  106. void             DoReceiveData(MyEndpointRef *myEp);
  107. OSStatus         DoDisconnect(MyEndpointRef *myEp);
  108. OSStatus         DoOrderlyDisconnect(MyEndpointRef *myEp);
  109. OSStatus         PollQueueList(void);
  110. OSErr             ProcessIncomingDataFile(MyEndpointRef *theEp, PacketPtr packetPtr);
  111. Boolean         TestDataIsPSQuery(PacketPtr packetPtr);
  112. OSStatus         ProcessPSQuery(PacketPtr packetPtr);
  113. Boolean            DoProcessPSQuery(PacketPtr packetPtr);
  114. OSErr             CheckFileToClose(void);
  115. void            DoServer(void);
  116. void            DoEvent(EventRecord *event);
  117. OSStatus         DoBind(EndpointRef ep, UInt8 socket, UInt8 type, OTQLen qlen, 
  118.                         char *nbpName, UInt32 nbpNameLen);
  119. OSStatus         ActivatePAPEndpoint(MyEndpointRef *myEp);
  120. OSStatus         InitPAPServerStuff(void);
  121. void            ClosePAPServerStuff(void);
  122. OSStatus            InitMyEndpointRef(MyEndpointRef *myEp);
  123. OSStatus         InitPAPBuffers(void);
  124. void             ReleasePAPMemory(void);
  125. extern OSStatus DoNegotiateEOMOption(EndpointRef ep, Boolean enableEOM);
  126. extern OSStatus DoSetServerStatusOption(EndpointRef ep, char *statusStr);
  127. extern OSStatus DoNegotiateSelfSendOption(EndpointRef ep, long enableSelfSend);
  128. void             DoValueBreak(long value, const char* message);
  129. OSStatus         BeginSetServerStatusOption(EndpointRef ep, UInt16 whichStr);
  130. UInt32             GetYesNoOption(void);
  131. Boolean         EndpointsAllBusy(void);
  132. #if HANDOFF_EP
  133. MyEndpointRef*    FindFreeHandoffEp(void);
  134. #endif
  135.  
  136. /*******************************************************************************
  137. ** EventHandler
  138. **  The event handler can be called at times when it is not safe to do console I/O,
  139. **  so this routine takes the event and jams it on a list to be handled when
  140. **  we know it's safe to print informative messages
  141. ********************************************************************************/
  142.  
  143. pascal void EventHandler(void* contextPtr, OTEventCode code,
  144.                                         OTResult result, void* cookie)
  145. {
  146.     #pragma unused(result,cookie)
  147.     switch (code)
  148.     {
  149.         case T_LISTEN:                /* An connection request is available     */
  150.             SetListenPendFlag(((MyEndpointRef*)contextPtr)->flags);
  151.             DoListenAccept(contextPtr);
  152.             break;
  153.             
  154.         //    T_DATA:
  155.         //
  156.         //    The main rule for processing T_DATA's is to remember that once you have
  157.         //    a T_DATA, you won't get another one until you have read to a kOTNoDataErr.
  158.         //    The advanced rule is to remember that you could get another T_DATA
  159.         //    during an OTRcv() which will eventually return kOTNoDataErr, presenting
  160.         //    the application with a synchronization issue to be most careful about.
  161.         //    
  162.         //    In this application, since an OTRcv() calls are made from inside the notifier,
  163.         //    this particular synchronization issue doesn't become a problem.
  164.         //
  165.         case T_DATA:                /* Standard data is available            */
  166.                                     // check that we are in the DATAXFER state
  167.             SetHasDataFlag(((MyEndpointRef*)contextPtr)->flags);
  168.             if (TstPassconFlag(((MyEndpointRef*)contextPtr)->flags))
  169.                 DoReceiveData(contextPtr);
  170.             break;
  171.  
  172.         case T_DISCONNECT:            /* A disconnect is available            */
  173.                 // check whether there is a file open on this endpoint
  174.             DoDisconnect(contextPtr);
  175.             break;
  176.             
  177.         case T_ERROR:                /* obsolete/unused in library            */
  178.         case T_UDERR:                /* A Unit Data Error has occurred        */
  179. #if SHOW_DEBUG_FLOW
  180.             DebugStr((const unsigned char *)"\p T_ERROR called;g");
  181. #endif
  182.             break;
  183.  
  184.         case T_ORDREL:                /* An orderly release is available        */
  185.             DoOrderlyDisconnect(contextPtr);
  186.             break;
  187.  
  188.         case T_GODATA:                /* Flow control lifted on standard data    */
  189.                                     // for this sample, I do not expect this event to occur.
  190. #if SHOW_DEBUG_FLOW
  191.             DebugStr((const unsigned char *)"\p T_GODATA called;g");
  192. #endif
  193.             break;
  194.  
  195.         case T_GOEXDATA:            /* Flow control lifted on expedited data*/
  196.                                     // for this sample, I do not expect this event to occur.
  197. #if SHOW_DEBUG_FLOW
  198.             DebugStr((const unsigned char *)"\p T_GOEXDATA called;g");
  199. #endif
  200.             break;
  201.  
  202.         case T_PASSCON:                /* State is now case T_DATAXFER            */
  203.                     // set the passcon flag
  204.                 // set flag to indicate that the endpoint is busy
  205.             SetEPBusyFlag(((MyEndpointRef*)contextPtr)->flags);
  206.                 // set flag to teel system to set server status flag appropriately
  207.                 // for multiple handoff endpoint servers, this would need to be
  208.                 // handled differently.
  209.             SetCheckOptFlag(gFlags);
  210.  
  211.             SetPassconFlag(((MyEndpointRef*)contextPtr)->flags);
  212.  
  213.                         // Set the timer for this connection
  214.             OTGetTimeStamp(&((MyEndpointRef*)contextPtr)->timerDog);
  215.  
  216.                     // check to see if there is any data to process
  217.             DoReceiveData(contextPtr);
  218.             break;
  219.             
  220.         case T_RESET:                /* Protocol has been reset                */
  221. #if SHOW_DEBUG_FLOW
  222.             DebugStr("\p T_RESET called;g");
  223. #endif
  224.             break;
  225.  
  226.         case T_ACCEPTCOMPLETE:        /* Accept call is complete                */
  227. #if SHOW_DEBUG_FLOW
  228.             DebugStr("\p T_ACCEPTCOMPLETE called;g");
  229. #endif
  230.             ClrAcceptPendFlag(((MyEndpointRef*)contextPtr)->flags);
  231.             break;
  232.  
  233.         case T_DISCONNECTCOMPLETE:    /* Disconnect call is complete            */
  234.             break;
  235.         
  236.         case T_UNBINDCOMPLETE:
  237. #if SHOW_DEBUG_FLOW
  238.             DebugStr("\p T_UNBINDCOMPLETE called;g");
  239. #endif
  240.             SetCheckOptFlag(gFlags);
  241.             ClrEPBusyFlag(((MyEndpointRef*)contextPtr)->flags);
  242.  
  243.             SetStatusIdleFlag(gFlags);
  244.  
  245.             ClrEPBoundFlag(((MyEndpointRef*)contextPtr)->flags);    // is the server endpoint bound
  246.             break;
  247.             
  248.         //
  249.         //    kStreamIoctlEvent:
  250.         //
  251.         //    This event is returned when an I_FLUSH ioctl has completed.
  252.         //    The flush was done in an attempt to get back all T_MEMORYRELEASED events
  253.         //    for outstanding OTSnd() calls with Ack Sends.   Errors are ignored at this point since it is
  254.         //    possible that the connection will already be gone, etc.
  255.         //
  256.         case kStreamIoctlEvent:
  257.             return;
  258.             break;
  259.         
  260.         case T_OPTMGMTCOMPLETE:
  261.             gOptionCompleted = 1;
  262.             break;
  263.  
  264.         default:
  265.             break;
  266.     }
  267. }
  268.  
  269. //
  270. //    EnterListenAccept
  271. //
  272. //    This is a front end to DoListenAccept() which is used whenever 
  273. //    it is not being called from inside the listener endpoint's notifier.
  274. //    We do this for syncrhonization.   If we were processing an OTListen()
  275. //    or an OTAccept() and we were interrupted at the listener endpoint's
  276. //    notifier with a T_LISTEN, etc, it would be inconvenient and would require
  277. //    some more sophisticated synchronization code to deal with the problem.
  278. //    The easy way to avoid this is to do an OTEnterNotifier() on the listener's
  279. //    endpoint.   
  280. //
  281. //    Important note - doing OTEnterNotifier on one endpoint only prevents that
  282. //    endpoint's notifier for interrupting us.   Since the same notifier code
  283. //    is used for lots of endpoints here, remember that the one endpoint's 
  284. //    notifier can interrupt another.   Doing an OTEnterNotifier() on the
  285. //    listener endpoint prevents the listener from interrupting us, but it
  286. //    does not prevent the Notifier() routine from interrupting us via 
  287. //    another endpoint which also uses the same routine.
  288. //
  289. //    Important note #2 - Don't ever do an OTEnterNotifier on an acceptor endpoint
  290. //    before doing the OTAccept().   This confuses OT and creates problems.
  291. //
  292. static void EnterListenAccept(MyEndpointRef *myServerEp)
  293. {
  294.     Boolean    doLeave;
  295.     
  296.     doLeave = OTEnterNotifier(myServerEp->ep);
  297.     DoListenAccept(myServerEp);
  298.     if (doLeave)
  299.         OTLeaveNotifier(myServerEp->ep);
  300. }
  301.  
  302. /*
  303.     DoListenAccept is designed to handle an incoming connect request.  This module demonstrates a
  304.     very complex task that all X/OPEN style acceptors must implement.  The problem stems from
  305.     fact that an endpoint may be hit by a number of simultaneous connect requests.  In addition
  306.     if there is a connection already established and the endpoint handles multiple
  307.     handoff endpoints, then there could also be an incoming disconnect request to process before
  308.     one can issue either an Accept or Disconnect response, all pending Connect requests must
  309.     be consumed from the streamhead first.  This places a burden on the accept routine to have
  310.     a place to temporarily stash all of the addresses to which discon messages will have to be
  311.     sent.
  312.     
  313.     Refer to Tech Note 1059 for the 8 steps to handling an incoming connection request.
  314.     
  315.     Note that once all of the connect request have been consumed and we are in the process of
  316.     sending discon responses, it could be that there will again be an kOTLookErr, which
  317.     indicates that another incoming connect request heeds to be consumed before
  318.     resuming with the discon responses.
  319.     
  320.     In this sample we consume the first listen request.  We then check whether we can accept
  321.     the incoming request, If so, then call OTAccept.  If OTAccept fails with a lookErr, then 
  322.     there is another connect request to be consumed.
  323.     
  324.     processed.
  325.     
  326. */
  327. void DoListenAccept(MyEndpointRef *myServerEp)
  328. {
  329.     MyEndpointRef    *myAcceptEp;
  330.     OSStatus    err;
  331.     TDiscon        discon;
  332.     OTResult    result;
  333.     TCall        call;
  334.     Boolean        done = false;
  335.     
  336.         // check we have already entered DoListenAccept from the main event loop and are 
  337.         // trying to do so again from the secondary interrupt
  338.     if (OTAtomicSetBit(&myServerEp->semaphore, kInListenLoop))
  339.         return;    // if the bit was previous set, then we already have entered, but not exitted
  340.                 // this proc from the main event loop
  341.  
  342.     if (TstAcceptPendFlag(myServerEp->flags))
  343.     {
  344.             // we're waiting for an Accept call to complete, so don't accept a new
  345.             // connection request as we will get an out of state error
  346.             // set the ListenPendFlag so we will check the server endpoint later
  347.         
  348.         SetListenPendFlag(myServerEp->flags);
  349.         done = true;
  350.     }
  351.     else
  352.         ClrListenPendFlag(myServerEp->flags);
  353.     
  354.     if (done == false)
  355.     {
  356.             // clear out the TCall structure
  357.         memset(&call, 0, sizeof(TCall));
  358.         
  359.             // set up the TCall structure
  360.         call.addr.maxlen = sizeof(struct DDPAddress);
  361.         call.addr.len = sizeof(struct DDPAddress);
  362.         call.addr.buf = (unsigned char *) &gAddr;
  363.                 
  364.         if (OTIsSynchronous(myServerEp->ep) == true)
  365.             OTSetAsynchronous(myServerEp->ep);
  366.             
  367.             // step 1 for handling an incoming connection request
  368.         err = OTListen(myServerEp->ep, &call);
  369.         
  370.         if (err == kOTNoDataErr)
  371.         {
  372. #if SHOW_DEBUG_FLOW
  373.             DebugStr("\p kOTNoDataErr returned by OTListen");
  374. #endif
  375.                 // don't need to do anything
  376.         }
  377.         else if (err == kOTLookErr)
  378.         {
  379.                 // step 2 for handling incoming connection requests
  380.                 // handle disconnect indications for a pending connection request
  381.                 // the only look indication allowed on an OTListen call is
  382.                 // T_DISCONNECT
  383.             result = OTLook(myServerEp->ep);
  384.             if (result == T_DISCONNECT)
  385.             {
  386.                     // this is what we were expecting
  387.                     // the disconnect is for an outstanding listen request
  388.                 err = OTRcvDisconnect(myServerEp->ep, &discon);
  389.                     
  390.             }
  391.             else
  392.             {
  393. #if SHOW_DEBUG_FLOW
  394.                 DebugStr((const unsigned char *)"\p unknown event returned by OTLook");
  395. #endif
  396.             }
  397.             done = true;
  398.              
  399.         }
  400.         else if (err != kOTNoError) // there was an unknown error in doing the listen
  401.         {
  402.             DoValueBreak((long)err, "error occured on OTListen #");
  403.             done = true;
  404.  
  405.         }
  406.     }
  407.     
  408.     if (done == false)
  409.     {
  410. #if HANDOFF_EP
  411.         myAcceptEp = FindFreeHandoffEp();
  412. #else
  413.         if (TstEPBusyFlag(myServerEp.flags) == false)
  414.             myAcceptEp = myServerEp;
  415.         else
  416.             myAcceptEp = nil;
  417. #endif         
  418.     }
  419.     
  420.     if (myAcceptEp == nil)
  421.     {
  422.             // if there are no available handoff endpoints, then send the disconnect
  423.         OTSndDisconnect(myServerEp->ep, &call);
  424.         done = true;
  425.     }
  426.     
  427.     if (done == false)
  428.     {
  429.     
  430.             // set a flag to indicate that an accept call is pending
  431.             // if we try to accept another connection before this
  432.             // accept call is completed, an out of state error results 
  433.             // need to do this before we make the OTAccept call
  434.         SetAcceptPendFlag(myServerEp->flags);
  435.         
  436.             // make the accept call and handoff the connection
  437.         err = OTAccept(myServerEp->ep, myAcceptEp->ep, &call);
  438.  
  439. #if SHOW_DEBUG_FLOW
  440.             DoValueBreak((long)err, "OTAccept returned #");
  441. #endif
  442.  
  443.         if (err == kOTNoError)
  444.         {
  445.             SetEPBusyFlag(myAcceptEp->flags);
  446.  
  447.                 // set flag to have server check the serverstatus
  448.             TstCheckOptFlag(gFlags);
  449.                         
  450.         }
  451.         else if (err == kOTLookErr)
  452.         {
  453.                 // an OTLookErr for an OTAccept call means that there is 
  454.                 // either an T_LISTEN or T_DISCONNECT event
  455.                 
  456.                 // check what event is on the pipe
  457.             result = OTLook(myServerEp->ep);
  458.             if (result == T_DISCONNECT)
  459.             {
  460.                 err = OTRcvDisconnect(myServerEp->ep, &discon);
  461.                     
  462.             }
  463.         
  464.         }
  465.         else
  466.         {
  467. #if HANDOFF_EP
  468.             DoValueBreak((long)err, "error occured on OTAccept for handoff #");
  469. #else
  470.             DoValueBreak((long)err, "error occured on OTAccept #");
  471. #endif
  472.  
  473.         }
  474.     }
  475.     
  476.         
  477.     OTAtomicClearBit(&myServerEp->semaphore, kInListenLoop);    // clear the bit that indicates we're in this loop
  478.     
  479. }
  480.  
  481. /*******************************************************************************
  482. ** ReceiveOnePacket - makes the call to receive just one packet
  483. ** If there is data, then the data is placed into the packetPtr
  484. ** and the incoming byte count is incremented
  485. ** if the kOTNoDataErr occurs, then the HasDataFlag is cleared.
  486. ** 
  487. **   
  488. ********************************************************************************/
  489. Boolean ReceiveOnePacket(MyEndpointRef *myEp, PacketPtr packetPtr)
  490. {
  491.     UInt32        localFlag = 0;
  492.     OTResult     result, res;
  493.     Boolean        done = false;
  494.                     
  495.     result = OTRcv(myEp->ep, &packetPtr->data, kPAPDataSize, &packetPtr->flags);
  496.     if (result < 0)
  497.     {
  498.             // requeue the buffer to the freeq
  499.         OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink));
  500.         done = true;
  501.         
  502.         if (result == kOTLookErr)
  503.         {
  504.             res = OTLook(myEp->ep);
  505.                 // for an OTRcv call, there are only 2 OTLook responses
  506.             if (res == T_DISCONNECT)
  507.             {
  508.                 res = DoDisconnect(myEp->ep);
  509.                     // if we get a disconnect event, then there will be no more
  510.                     // data in the pipe.
  511. #if SHOW_DEBUG_FLOW
  512.                 DebugStr("\p processing disconnect in OTRcv;g");
  513. #endif
  514.                 ClrOrdDisconFlag(myEp->flags);
  515.                 ClrInPSQueryFlag(myEp->flags);
  516.             }
  517.             else if (res == T_ORDREL)
  518.             {
  519. #if SHOW_DEBUG_FLOW
  520.                 DebugStr("\p processing orderly disconnect in OTRcv;g");
  521. #endif
  522.                 res = DoOrderlyDisconnect(myEp->ep);
  523.                 localFlag = 1;
  524.             }
  525.             
  526.         }
  527.         else if (result == kOTOutStateErr)
  528.         {
  529.                 // client could have issued a disconnect call while this call was about to
  530.                 // to be processed
  531.             ClrHasDataFlag(myEp->flags);
  532.         }
  533.         
  534.         else if (result == kOTNoDataErr)
  535.         {
  536.                 // no more data so clr the flag
  537.             ClrHasDataFlag(myEp->flags);
  538.         }
  539.         else 
  540.         {
  541. #if SHOW_DEBUG_FLOW
  542.             DebugStr("\pUnknown error occurred in OTRcv");
  543. #endif
  544.         }
  545.     }
  546.     else if (result == 0)
  547.     {
  548. #if SHOW_DEBUG_FLOW
  549.         DebugStr("\p OTRcv got nothing ;g");
  550. #endif
  551.         
  552.         if (TstInPSQueryFlag(myEp->flags))    // check if the endpoint is already processing a postscript
  553.         {
  554. #if SHOW_DEBUG_FLOW
  555.             DebugStr("\p 0 byte packet while processing ps query ;g");
  556. #endif
  557. #if SHOW_DEBUG_FLOW
  558.             if (packetPtr->flags & T_MORE == 0)
  559.                 DebugStr("\p T_MORE was not set;g");
  560.             else
  561.                 DebugStr("\p T_MORE was set;g");
  562. #endif
  563.                 // send an empty response with EOF flasg set since it appears that this is what the Laserwriter 
  564.                 // client wants to see.
  565.             packetPtr->theEp = myEp;
  566.             SendEmptyPacket(packetPtr);
  567.                 // clear bit that indicates we are processing a postscript query
  568.             ClrInPSQueryFlag(myEp->flags);
  569. #if SHOW_DEBUG_FLOW
  570.     
  571.             DebugStr("\p Have finished ps query;g");
  572. #endif
  573.         }
  574.         
  575.             // requeue the buffer to the freeq since no data was returned
  576.         OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink));
  577.     }
  578.     else
  579.     {
  580.  
  581.         if (TstdumpPktsFlag(gFlags))
  582.         {
  583.             OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink));
  584.         }
  585.         else
  586.         {
  587.             
  588.                         // save the endpoint ref associated with this data
  589.             packetPtr->theEp = myEp;
  590.             
  591.                         // save the number of bytes read
  592.             packetPtr->numBytes = result;
  593.             
  594.                         // intialize the lastPos field
  595.             packetPtr->lastPos = 0;
  596.             
  597.                         // get the timestamp for this packet
  598.             OTGetTimeStamp(&packetPtr->timeStamp);
  599.             
  600.                         // Set the timer for this connection
  601.             OTGetTimeStamp(&myEp->timerDog);
  602.             
  603.             if (!DoProcessPSQuery(packetPtr))
  604.             {
  605.                     // packet is not a postscript query so queue to the endpoint usedQ
  606.                     // queue the buffer to the usedQ associated with the endpoint
  607.                 OTLIFOEnqueue(myEp->usedQ, &(packetPtr->fLink));
  608.                 
  609.             }
  610.         
  611.         }
  612.  
  613.     }
  614.     
  615.     return done;
  616.         
  617. }
  618.  
  619.  
  620. /*******************************************************************************
  621. ** DoReceiveData
  622. **   
  623. ********************************************************************************/
  624. void DoReceiveData(MyEndpointRef *myEp)
  625. {
  626.     PacketPtr    packetPtr;
  627.     OTFlags        flags = 0;
  628.     UInt32        localFlag = 0;
  629.     Boolean        done;
  630.     
  631.         // check we have already entered DoReceiveData from the main event loop
  632.         // or from the event handler and are 
  633.         // trying to do so again a second time
  634.     if (OTAtomicSetBit(&myEp->semaphore, kInRcvDataFlag))
  635.         return;    // if the bit was previous set, then we already have entered, but not exitted
  636.                 // this proc from the main event loop
  637.  
  638.     done = gDone;
  639.     while (done == false)
  640.     {
  641.         if (gFreeQ->fHead != nil)
  642.         {
  643.             packetPtr = (PacketPtr)OTLIFODequeue(gFreeQ);
  644.             done = ReceiveOnePacket(myEp, packetPtr);
  645.         }
  646.         else
  647.         {
  648.             
  649.             done = true;
  650.         }
  651.             
  652.         
  653.     }
  654.  
  655.     OTAtomicClearBit(&myEp->semaphore, kInRcvDataFlag);    
  656.                 // clear the bit that indicates we're in this loop
  657.                         
  658. }
  659.  
  660.  
  661. OSStatus DoDisconnect(MyEndpointRef *myEp)
  662. {
  663.     OSStatus        err;
  664.     
  665.  
  666.     err = OTRcvDisconnect(myEp->ep, NULL);
  667.     if (err != kOTNoError)
  668.     {
  669. #if SHOW_DEBUG_FLOW
  670.         DoValueBreak((long)err, "error occured on OTRcvDisconnect #");
  671. #endif
  672.     }
  673.     else
  674.     {
  675.         if (TstAcceptPendFlag(myEp->flags))
  676.             ClrAcceptPendFlag(myEp->flags);
  677.     }
  678.  
  679.     return err;
  680. }
  681.  
  682. OSStatus DoOrderlyDisconnect(MyEndpointRef *myEp)
  683. {
  684.     OSStatus        err;
  685.     
  686.     err = OTRcvOrderlyDisconnect(myEp->ep);
  687.     if (err < kOTNoError)
  688.     {
  689. #if SHOW_DEBUG_FLOW
  690.         DoValueBreak((long)err, "error occured on OTRcvOrderlyDisconnect #");
  691. #endif
  692.     }
  693.     else if (err > 0)
  694.     {            
  695. #if SHOW_DEBUG_FLOW
  696.         DebugStr("\p OTRcvOrderlyDisconnect returned positive result;g");
  697. #endif
  698.         SetOrdDisconFlag(myEp->flags);
  699.     }
  700.     else
  701.     {
  702.         SetOrdDisconFlag(myEp->flags);
  703.     }
  704.     
  705.     return err;
  706. }
  707.  
  708. /*******************************************************************************
  709. ** PollEventList
  710. **  This routine checks the used queue to see whether the handler has processed
  711.     incoming PAP data from the client.
  712. **    If the used queue is not empty, there are two different options to deal with.
  713.     First we check whether we are in the middle of processing a postscript data
  714.     file.  If so, we continue to read the data into a file which we have created.
  715.     If not currently processing postscript data, we then check to see if the incoming
  716.     packet is part of a postscript query in progress or is the beginning of a 
  717.     postscript query.  If so, we continue to read the query and respond as appropriate.
  718.     If not a query, then this must be the first packet of postscript data being sent.
  719. ********************************************************************************/
  720.  
  721. OSStatus PollQueueList(void)
  722. {
  723.     PacketPtr    packetPtr, nextPacketPtr;
  724.     MyEndpointRef *theEp;
  725.     OSStatus    err = kOTNoError;
  726.     short        i = 0;
  727.     Boolean        done = false;
  728.  
  729. #if SHOW_DEBUG_FLOW
  730.     short        num = 0;
  731.     Boolean        inLoop = false;
  732. #endif
  733.     
  734. #if HANDOFF_EP
  735.     for (i = 0; (i < kMaxHandoffEPs) && (err == kOTNoError); i++)
  736.     {
  737.         theEp = &gHandoffEp[i];
  738. #else
  739.         theEp = &gEp;
  740. #endif
  741.  
  742.         packetPtr = (PacketPtr)OTLIFOStealList(theEp->usedQ);
  743.         while (packetPtr != nil)
  744.         {
  745.  
  746. #if SHOW_DEBUG_FLOW
  747.             inLoop = true;
  748. #endif
  749.             packetPtr = (PacketPtr)OTReverseList((OTLink*)packetPtr);
  750.             do
  751.             {
  752. #if SHOW_DEBUG_FLOW
  753.                 num++;
  754. #endif
  755.                 nextPacketPtr = (PacketPtr)packetPtr->fLink.fNext;
  756.                 
  757.                 err = ProcessIncomingDataFile(theEp, packetPtr);
  758.         
  759.                     // queue the packetPtr back to the freeQ
  760.                 OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink));
  761.  
  762.                 packetPtr = nextPacketPtr;
  763.                 
  764.             } while((packetPtr != nil) && (err == kOTNoError));
  765.             
  766.             packetPtr = (PacketPtr)OTLIFOStealList(theEp->usedQ);
  767.         }
  768.  
  769. #if HANDOFF_EP
  770.     }
  771. #endif
  772.                 
  773. #if SHOW_DEBUG_FLOW
  774.     if (inLoop)
  775.     {
  776. //        fprintf(stdout, "numbers packets processed by PollQueueList %d\n", num);
  777.     }
  778. #endif
  779.     
  780.     return err;
  781. }
  782.  
  783. OSErr ProcessIncomingDataFile(MyEndpointRef *theEp, PacketPtr packetPtr)
  784. {
  785.     OSStatus        err = noErr;
  786.     short            fRefNum;
  787.     
  788.     
  789.         // For each incoming packet, we need to check whether it
  790.         // contains postscript query requests, or if we have not yet
  791.         // received the EOF
  792.  
  793.     if (!TstdumpPktsFlag(gFlags))    // check if we are dumping the packets
  794.     {
  795.         if (!TstTempFileFlag(theEp->flags)) // is there a temp file opened for this endpoint
  796.         {
  797.                 // no, so create one
  798.             err = OpenTempFile(&fRefNum);
  799.             if (err == noErr)
  800.             {
  801.                 fprintf(stdout, "processing incoming file\n");
  802.                 theEp->fRefNum = fRefNum;        // save the refnum of the temp file;
  803.                 SetTempFileFlag(theEp->flags);    // indicate that there is a temp file associated
  804.                                                 // with this endpoint.
  805.                     // set the time Data In timestamp field
  806.                 BlockMove((Ptr)&packetPtr->timeStamp, (Ptr)&theEp->timeDataIn, sizeof(OTTimeStamp));
  807.                     // reset the numBytesIn field to zero
  808.                 theEp->numBytesIn = 0;
  809.             }
  810.             
  811.         }
  812.     }
  813.  
  814.  
  815.     if (err == noErr)
  816.     {
  817.         
  818.         if (!TstdumpPktsFlag(gFlags))    // check if we are dumping the packets
  819.         {
  820.                 // the temp file has been created or already exists, so read the data and write it
  821.                 // to the temp file
  822.             err = WriteDataToTempFile(theEp->fRefNum, packetPtr->data, packetPtr->numBytes);
  823.         }
  824.         
  825.         if (err != noErr)
  826.         {
  827. #if SHOW_DEBUG_FLOW
  828.             DoValueBreak((long)err, "error occured writing data to tempfile #");
  829. #endif
  830.         }
  831.         else
  832.         {
  833.                 // set the time data end timestamp field
  834.             BlockMove((Ptr)&packetPtr->timeStamp, (Ptr)&theEp->timeDataEnd, sizeof(OTTimeStamp));
  835.                 // add the number of bytes in the packet to the numBytesIn field
  836.             theEp->numBytesIn += packetPtr->numBytes;
  837.  
  838.                 // check if we've read the last packet for the file
  839.             if ((packetPtr->flags & T_MORE) == 0)
  840.             {
  841.                 if (!TstdumpPktsFlag(gFlags))
  842.                 {
  843.                     fprintf(stdout, "T_MORE flag not set \n");
  844.                     
  845.                     // the proper way to detect the end of a file is to look for
  846.                     // the EOF flag at the end of the file.
  847.                     // It also appears that looking for the T_MORE flag to be clear
  848.                     // is not a reliable way to detect the end of a file.
  849.                     // When a status request is sent by the client that is sending
  850.                     // data, the T_MORE flag appears to be cleared for the incoming 
  851.                     // packet.
  852.                 }
  853.                 
  854.             }
  855.                 
  856.         }
  857.     }
  858.     
  859.     return err;
  860. }
  861.  
  862.  
  863.  
  864.  
  865. OSErr CheckFileToClose(void)
  866. {
  867.     OSErr    err = noErr;
  868.     
  869.     if (gFRefNumToClose != 0)
  870.     {
  871.         fprintf(stdout, "\n\nAm about to close file with fRefNum %ld.\n", gFRefNumToClose);
  872.         
  873.         err = CloseTempFile(gFRefNumToClose);
  874.         if (err != noErr)
  875.             fprintf(stdout, "\n\nError occurred closing file - error %ld.\n", err);        
  876.         
  877.             // reset the tempfile to close so that we don't close this file again
  878.         gFRefNumToClose = 0;
  879.     }
  880.     
  881.     return err;
  882. }
  883.  
  884.  
  885. /*******************************************************************************
  886. ** DoServer
  887. ********************************************************************************/
  888.  
  889. void DoServer(void)
  890. {
  891.     EventRecord        theEvent;
  892.     OTTimeStamp        eTime;
  893.     OTResult        state;
  894.     UInt32            eTimeMSec, sec, msec, rate;
  895.     MyEndpointRef    *theEp;
  896.     OSStatus         err1, err = kOTNoError;
  897. #if HANDOFF_EP
  898.     short            i = 0;
  899. #endif
  900.     
  901.     while (gDone == false)
  902.     {
  903.         if (!WaitNextEvent(everyEvent, &theEvent, 15, nil))
  904.             theEvent.what = nullEvent;
  905.             
  906.         DoEvent(&theEvent);
  907.         
  908.         if (TstListenPendFlag(gEp.flags))        // are there incoming connect requests to process
  909.         {
  910.             fprintf(stdout, "\n processing EnterListenAccept from even tloop");
  911.             EnterListenAccept(&gEp);
  912.         }
  913.             
  914.         err = PollQueueList();
  915.         if (err != kOTNoError)        // we're outta here if an error occurred.
  916.             gDone = true;
  917.  
  918. #if HANDOFF_EP
  919.         theEp = &gHandoffEp[i];
  920.         i++;
  921.         if (i >= kMaxHandoffEPs)
  922.             i = 0;
  923. #else
  924.         theEp = &gEp;
  925. #endif
  926.             // get the current endpoint state
  927.         state = OTGetEndpointState(theEp->ep);
  928.         
  929.         switch (state)
  930.         {
  931.             case T_DATAXFER:
  932.                     
  933.                     // check if we haven't completed reading data from OT.  
  934.                 DoReceiveData(theEp);
  935.                 break;
  936.                 
  937.             case T_INREL:    // we've processed an incoming Orderly Release request
  938.                             // but have not issued our own orderly disconnect
  939.                 
  940.  
  941.                     // note that since we have already processed an incoming orderly disconnect
  942.                     // we are in the INREL state.  After making the OTSndOrderlyDisconnect
  943.                     // call, there will not be a T_DISCONNECTCOMPLETE call, since we complete
  944.                     // the orderly release with the above call.  We go ahead and reset our flags
  945.                     // and issue the OTUnbind call if we are using a handoff endpoint.
  946.                     // if we did get the orderly release indication, then make sure we have
  947.                     // drained the receive queue - PollQueueList does this
  948.  
  949.                 do
  950.                 {
  951.                     fprintf(stdout, "processing some last data\n");
  952.                     PollQueueList();
  953.                 
  954.                         // check if we haven't completed reading data from OT. 
  955.                     DoReceiveData(theEp);
  956.                     
  957.                 }while( (TstHasDataFlag(theEp->flags)) || (theEp->usedQ->fHead != nil));
  958.  
  959.                     // clear the flag and send a corresponding disconnect
  960.                 err = OTSndOrderlyDisconnect(theEp->ep);
  961.                 if (err)
  962.                 {
  963.                     // the only error that makes sense here is the kOTLookErr
  964.                     //  which the only event is the T_DISCONNECT event.  This should not happen,
  965.                     // however, if there are multiple handoffs, this may be an issue.  Given the way
  966.                     // incoming requests are handled, I don't expect that there would be an 
  967.                     // unresponded listen request here that would warrant a possible T_DISCONNECT
  968.                     // however some implementation will need to consider this possibility.
  969. #if SHOW_DEBUG_FLOW
  970.                     DoValueBreak((long)err, "error occured on OTSndOrderlyDisconnect #");
  971.                     
  972.                     DebugStr("\p quitting program");
  973. #endif
  974.                     gDone = true;
  975.  
  976.                 }
  977.                                     
  978.                 ClrOrdDisconFlag(theEp->flags);
  979.     
  980.                 if (TstTempFileFlag(theEp->flags))
  981.                 {    
  982.                                                         // is there an open spool file associated with this endpoint
  983.                     gFRefNumToClose = theEp->fRefNum;    // set the global to have this file closed
  984.                                                     // note that this method of signalling for a file to
  985.                                                     // get closed is a quick and dirty method and assumes th
  986.                                                     // that only one file at a time is being processed here.
  987.                     CheckFileToClose();
  988.                 }
  989.                 
  990.                 ClrTempFileFlag(theEp->flags);
  991.                 ClrPassconFlag(theEp->flags);
  992.  
  993. #if HANDOFF_EP
  994.                     // in order to re-use a handoff endpoint, it must be unbound.  Unbinding the 
  995.                     // endpoint clears the connection information that allows to to know which
  996.                     // endpoint, incoming PAP data is routed to.
  997.                 if (theEp->ep != gEp.ep)
  998.                 {
  999.                     OTUnbind(theEp->ep);
  1000.     
  1001.                 }
  1002. #else
  1003.                     // indicate that we are no longer busy
  1004.                 ClrEPBusyFlag(theEp->flags);
  1005.                     // set flag so that we will check the status
  1006.                 SetCheckOptFlag(gFlags);
  1007.                 SetStatusIdleFlag(gFlags);
  1008.                     
  1009. #endif
  1010.                     
  1011.                 
  1012.                     // calculate the elapsed time
  1013.                 OTSubtractTimeStamps(&eTime, &theEp->timeDataIn, &theEp->timeDataEnd);
  1014.                     // convert to millisecs
  1015.                 eTimeMSec = OTTimeStampInMilliseconds(&eTime);
  1016.                 sec = eTimeMSec / (UInt32)1000;
  1017.                 msec = eTimeMSec %(UInt32)1000;
  1018.                 fprintf(stdout, "Time to transfer file was %ld.%ld seconds.\n", sec, msec);
  1019.                 fprintf(stdout, "Bytes transferred was - %ld.\n", theEp->numBytesIn);
  1020.  
  1021.                 if (sec != 0)
  1022.                 {
  1023.                     rate = (theEp->numBytesIn / sec) / 1024;
  1024.                     fprintf(stdout, "Transfer rate - %ld KBytes/Sec. \n", rate);
  1025.                 }
  1026.                 else if (msec != 0)
  1027.                 {
  1028.                     rate = (theEp->numBytesIn *1000 / msec) / 1024;
  1029.                     fprintf(stdout, "Transfer rate - %ld KBytes/Sec. \n", rate);
  1030.                 }
  1031.                 break;
  1032.                 
  1033.             case T_IDLE:    // we're either really idle, or the we've processed a disconnect event
  1034.                             // if we processed a disconnect event then the busy flag for the ep would still be set
  1035.                 if (TstEPBusyFlag(theEp->flags))    // we're we previously connected
  1036.                 {
  1037.                     ClrPassconFlag(theEp->flags);    // clear the flag so that we can accept a new connection
  1038.                     ClrOrdDisconFlag(theEp->flags);
  1039.                     
  1040.                     ClrTempFileFlag(theEp->flags);
  1041.                     ClrPassconFlag(theEp->flags);
  1042.  
  1043.                     if (TstTempFileFlag(theEp->flags))    
  1044.                                                             // is there an open spool file associated with this endpoint
  1045.                         gFRefNumToClose = theEp->fRefNum;    // set the global to have this file closed
  1046.                                                         // note that this method of signalling for a file to
  1047.                                                         // get closed is a quick and dirty method and assumes th
  1048.                                                         // that only one file at a time is being processed here.
  1049.  
  1050. #if HANDOFF_EP
  1051.                         // in order to re-use a handoff endpoint, it must be unbound.  Unbinding the 
  1052.                         // endpoint clears the connection information that allows to to know which
  1053.                         // endpoint, incoming PAP data is routed to.
  1054.                     if (theEp->ep != gEp.ep)
  1055.                     {
  1056.                         OTUnbind(theEp->ep);
  1057.         
  1058.                     }
  1059. #else
  1060.                         // set flag so that we will check the status
  1061.                     SetCheckOptFlag(gFlags);
  1062.                         // indicate that we are no longer busy
  1063.                     ClrEPBusyFlag(theEp->flags);
  1064.                     SetStatusIdleFlag(gFlags);
  1065.  
  1066. #endif
  1067.                 }
  1068.                 break;
  1069.                 
  1070.         }
  1071.         
  1072.         
  1073.         err1 = CheckFileToClose();
  1074.         if (err1 != noErr)
  1075.             fprintf(stdout, "\n\nCheckFileToClose error - %ld.\n", err1);
  1076.     
  1077.         if (TstCheckOptFlag(gFlags))
  1078.         {
  1079.             
  1080.             if (EndpointsAllBusy())
  1081.             {
  1082.                 if (TstStatusBusyFlag(gFlags) == false)
  1083.                 {
  1084.                     BeginSetServerStatusOption(gEp.ep, kSetBusyStr);
  1085.                     SetStatusBusyFlag(gFlags);
  1086.                 }
  1087.             }
  1088.             else
  1089.             {
  1090.                 if (TstStatusBusyFlag(gFlags) == true)
  1091.                 {
  1092.                     BeginSetServerStatusOption(gEp.ep, kSetIdleStr);
  1093.                     ClrStatusBusyFlag(gFlags);
  1094.                 }
  1095.             }
  1096.             ClrCheckOptFlag(gFlags);
  1097.         }
  1098.                     
  1099.  
  1100.     }
  1101.  
  1102. }
  1103.  
  1104. /* Do the right thing for an event.  Determine what kind of event it is, and
  1105. ** call the appropriate routines. */
  1106.  
  1107. void    DoEvent(EventRecord *event)
  1108. {
  1109.     Point    pt;
  1110.     char    key;
  1111.  
  1112.     switch(event->what) 
  1113.     {
  1114.  
  1115.         case nullEvent:
  1116.         case mouseDown:
  1117.         case activateEvt:
  1118.         case updateEvt:
  1119.         case kHighLevelEvent:
  1120.             break;
  1121.  
  1122.         case autoKey:
  1123.         case keyDown:
  1124.             key = event->message & charCodeMask;
  1125.             switch (key)
  1126.             {
  1127.                 case 'q':
  1128.                 case 'Q':
  1129.                     if (event->modifiers & cmdKey)
  1130.                     gDone = true;
  1131.                     break;
  1132.             }
  1133.             break;
  1134.  
  1135.         case osEvt:
  1136.             switch ((event->message >> 24) & 0xFF) 
  1137.             {
  1138.                     /* Must logical and with 0xFF to get only low byte. */
  1139.                     /* High byte of message. */
  1140.  
  1141.                 case mouseMovedMessage:
  1142.                     break;
  1143.  
  1144.                 case suspendResumeMessage:
  1145.                         /* Suspend/resume is also an activate/deactivate. */
  1146.                     if ((event->message) & resumeFlag)
  1147.                         ClrInBackGndFlag(gFlags);    // process has come to the foreground
  1148.                     else
  1149.                         SetInBackGndFlag(gFlags);    // process has gone into background
  1150.                     break;
  1151.             }
  1152.             break;
  1153.  
  1154.         case diskEvt:
  1155.             if (HiWord(event->message) != noErr) 
  1156.             {
  1157.                 SetPt(&pt, kDILeft, kDITop);
  1158.                 DIBadMount(pt, event->message);
  1159.             }        /* It is not a bad idea to at least call DIBadMount in response to */
  1160.             break;    /* a diskEvt, so that the user can format a floppy. */
  1161.     }
  1162.  
  1163. }
  1164.  
  1165.  
  1166. /*******************************************************************************
  1167. ** DoBind
  1168. **    Implements the option to set the qlen field and to bing with an nbpName
  1169. **    by setting the nbpName field with a pointer to an entity name.
  1170. **     If nbpName is not nil, you must also set the nbpNameLen to the size of the
  1171. **    nbpName field. Note, nbpName is not a pascal nor a C string.  
  1172. ********************************************************************************/
  1173.  
  1174. OSStatus DoBind(EndpointRef ep, UInt8 socket, UInt8 type, OTQLen qlen, 
  1175.                     char *nbpName, UInt32 nbpNameLen)
  1176. {
  1177.     TBind            req, ret;
  1178.     OSStatus         err = kOTNoError;
  1179.     DDPNBPAddress    addr;
  1180.     
  1181.  
  1182.     // init the DDPAddress variable    
  1183.     if (nbpName == nil)        // are we registering with no nbp name
  1184.     {
  1185.         addr.fAddressType = AF_ATALK_DDP;        // set the address type
  1186.         req.addr.len    = kDDPAddressLength;
  1187.     }
  1188.     else
  1189.     {
  1190.         addr.fAddressType = AF_ATALK_DDPNBP;    // registering with an nbp name
  1191.                                                 // copy the name to the name buffer
  1192.         BlockMove(nbpName, &addr.fNBPNameBuffer, nbpNameLen);
  1193.                                                 // set the request length to include the name len
  1194.         req.addr.len    = kDDPAddressLength + nbpNameLen;
  1195.     }
  1196.     
  1197.     addr.fNetwork = 0;
  1198.     addr.fNodeID = 0;
  1199.     addr.fSocket = socket;
  1200.     addr.fDDPType = type;
  1201.     
  1202.     // set up the request
  1203.     req.addr.buf    = (UInt8*)&addr;
  1204.     req.qlen        = qlen;
  1205.     
  1206.     // set up the response
  1207.     ret.addr.buf    = (UInt8*)&addr;
  1208.     ret.addr.maxlen    = sizeof(addr);
  1209.  
  1210.     fprintf(stderr, "Doing Bind\n");
  1211.  
  1212.     //
  1213.     // Try to bind
  1214.     // 
  1215.     err = OTBind(ep, &req, &ret);
  1216.  
  1217.     if ( err != kOTNoError )
  1218.     {
  1219.         fprintf(stderr, "PAPSample: DoBind returns error %ld\n", err);
  1220.         return err;
  1221.     }
  1222.  
  1223.     fprintf(stderr, "Bound address = ");
  1224.     ShowDDPAddress((DDPAddress*)&addr);
  1225.     fprintf(stderr, "\n");
  1226.     fprintf(stderr, "Bound queue len is %ld\n", ret.qlen);
  1227.  
  1228.     fprintf(stderr, "After Bind, ");
  1229.     ShowEndpointState(ep, "");
  1230.  
  1231.     return err;
  1232. }
  1233.  
  1234.  
  1235.  
  1236. /*******************************************************************************
  1237. ** ActivatePAPEndpoint
  1238. ** 
  1239. ********************************************************************************/
  1240.  
  1241. OSStatus ActivatePAPEndpoint(MyEndpointRef *myEp)
  1242. {
  1243.     OSStatus    err = kOTNoError;
  1244.     
  1245.     //
  1246.     // Create a PAP endpoint
  1247.     // Open listener, using the tilisten module to make 
  1248.     // listen/accept/disconnect processing much simpler.
  1249.     //
  1250.     myEp->ep = OTOpenEndpoint(OTCreateConfiguration("tilisten, pap"), 0, NULL, &err);
  1251.  
  1252.     if ( myEp->ep == NULL || err != kOTNoError )
  1253.     {
  1254.         myEp->ep = NULL;
  1255.         
  1256.         if (err == kOTNoError)
  1257.             err = memFullErr;
  1258.             
  1259.         fprintf(stderr,"ERROR: OpenEndpoint(\"pap\") failed with %ld\n", err);
  1260.     }
  1261.     else
  1262.     {
  1263.         SetEPActiveFlag(myEp->flags);
  1264.     }
  1265.  
  1266.     /*-------------------------------------------------------------------------
  1267.     Enable self send
  1268.         have to do this before we set the endpoint into async mode.
  1269.     ------------------------------------------------------------------------- */
  1270.     if (err == kOTNoError)
  1271.     {
  1272.         err = DoNegotiateSelfSendOption(myEp->ep, 1);
  1273.         
  1274.         if (err <= kOTNoError)
  1275.         {
  1276.             fprintf(stderr, "error negotiating selfsend on main endpoint\n");
  1277.             err = kOTNoError;    // don't let this stop the app.
  1278.         }
  1279.         else
  1280.         {
  1281.             fprintf(stderr, "selfsend enabled\n");
  1282.             if (err == 0)
  1283.                 fprintf(stderr, "selfsend was previously off\n");
  1284.             else
  1285.                 fprintf(stderr, "selfsend was previously on\n");
  1286.         }
  1287.     }
  1288.  
  1289. #if HANDOFF_EP
  1290. #else
  1291.     // don't need EOM option
  1292.     if (err == kOTNoError)
  1293.     {
  1294.  
  1295.             // turn on the EOM option
  1296.         err = DoNegotiateEOMOption(myEp->ep, true);
  1297.         if (err != kOTNoError)
  1298.         {
  1299.             ClrEOMOnFlag(myEp->flags);
  1300.             fprintf(stderr, "\n\nError setting EOM option!\n");
  1301.         }
  1302.         else
  1303.         {
  1304.             SetEOMOnFlag(myEp->flags);
  1305.         }
  1306.     }
  1307. #endif
  1308.     
  1309.     if (err == kOTNoError)
  1310.     {
  1311.         //
  1312.         // Install notifier we're going to use for testing - we do this before
  1313.         //         binding - at this point, we're still synchronous
  1314.         //
  1315.         err = OTInstallNotifier(myEp->ep, EventHandler, (void*)myEp);
  1316.         if ( err != kOTNoError )
  1317.         {
  1318.             fprintf(stderr, "ERROR: InstallNotifier() failed with %ld\n for server endpoint", err);
  1319.         }
  1320.     }
  1321.         
  1322.     if (err == kOTNoError)
  1323.     {
  1324.  
  1325.         /*-------------------------------------------------------------------------
  1326.         Prepare to Bind the endpoint by getting the NBP Name
  1327.         ------------------------------------------------------------------------- */
  1328.  
  1329.         /*-------------------------------------------------------------------------
  1330.         Bind the endpoint endpoint.
  1331.         ------------------------------------------------------------------------- */
  1332.  
  1333.         err = DoBind(myEp->ep, kDynamicSocket, kATPType, 2, (char*)gServerNBPNameStr, clen((char*)gServerNBPNameStr));
  1334.         if ( err != kOTNoError )
  1335.         {
  1336.             fprintf(stderr, "PAPSample: bind of server endpoint failed.\n");
  1337.         }
  1338.         else
  1339.         {
  1340.             SetEPBoundFlag(myEp->flags);
  1341.             OTSetAsynchronous(myEp->ep);
  1342.         }
  1343.     }
  1344.  
  1345.     /*-------------------------------------------------------------------------
  1346.     Set the server status string
  1347.     ------------------------------------------------------------------------- */
  1348.     if (err == kOTNoError)
  1349.         BeginSetServerStatusOption(myEp->ep, kSetIdleStr);
  1350.     
  1351.     
  1352.     
  1353.     return err;
  1354. }
  1355.  
  1356. /*******************************************************************************
  1357. ** InitMyEndpointRef function - zero out the MyEndpointRef structure
  1358. **     Course we could use the memset function here.
  1359. ********************************************************************************/
  1360.  
  1361. OSStatus InitMyEndpointRef(MyEndpointRef *myEp)
  1362. {
  1363.     myEp->ep = nil;
  1364.     myEp->flags = 0;
  1365.     myEp->usedQ = OTAllocMem(sizeof(OTLIFO));
  1366.     if (myEp->usedQ == nil)
  1367.         return memFullErr;
  1368.     
  1369.     myEp->usedQ->fHead = nil;        // no packets with this endpoint.
  1370.     
  1371.     return kOTNoError;
  1372. }
  1373.  
  1374.  
  1375. OSStatus InitPAPBuffers(void)
  1376. {
  1377.     PacketPtr        packetPtr;
  1378.     OSStatus        err = kOTNoError;
  1379.     short            i;
  1380.     
  1381.         // allocate memory for the freeQ
  1382.     gFreeQ = OTAllocMem(sizeof(OTLIFO));
  1383.     if (gFreeQ == nil)
  1384.         return memFullErr;
  1385.         
  1386.     /* set up the free queue */
  1387.     gFreeQ->fHead = nil;
  1388.     
  1389.         // allocate a temp buffer to be used when packet data overlaps.
  1390.     gTempPackPtr = OTAllocMem(sizeof(PacketBuffer) + kPAPDataSize);    
  1391.     if (gTempPackPtr == nil)
  1392.         err = memFullErr;
  1393.  
  1394.     /* enqueue the packet buffer records to the free queue */
  1395.     for (i=0; (i<kNumBuffers) && (err == kOTNoError); i++)
  1396.     {
  1397.             // before calling OTAllocMem, we must have already have called
  1398.             // InitOpenTransport
  1399.             
  1400.         packetPtr = OTAllocMem(sizeof(PacketBuffer));
  1401.         if (packetPtr != nil)
  1402.         {
  1403.             packetPtr->flags = 0;
  1404.             packetPtr->fLink.fNext = nil;
  1405.             OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink));    // first field is fLink field
  1406.             
  1407.         }
  1408.         else
  1409.             err = memFullErr;
  1410.     }
  1411.     
  1412.     if (err == kOTNoError)
  1413.     {
  1414.         /* show that the buffers have been init'd */
  1415.         SetBufsInitdFlag(gFlags);
  1416.  
  1417.         /* note that we don't need to worry about holding memory in the
  1418.             presence of VM.  The notifier routine will be called at
  1419.             deferred task time when it is safe to page memory
  1420.          */
  1421.         
  1422.     }
  1423.     else
  1424.     {
  1425.         fprintf(stderr, "\n\nError allocating receive buffers!\n");
  1426.         fprintf(stderr, "\nBye Bye.\n");
  1427.     }
  1428.         
  1429.     return(err);
  1430.  
  1431. }
  1432.  
  1433.     
  1434.  
  1435. /*******************************************************************************
  1436. ** Initialize OpenTransport and call DoTest function
  1437. ********************************************************************************/
  1438. OSStatus InitPAPServerStuff(void)
  1439. {
  1440.     OSStatus    err = kOTNoError;
  1441.     UInt32        growAmt, selection;
  1442.     UInt32        i;
  1443.     char        mystr[255];    
  1444.                 
  1445.         // register ourselves with OT
  1446.     OTRegisterAsClient((OTClientName) "PAPServerSample", nil);
  1447.  
  1448.         // ask whether to alter current options
  1449.     fprintf(stdout, "\nDo you want to change the current options?");
  1450.     fprintf(stdout, "\n  Options to change 1. SetMemoryLimits");
  1451.     fprintf(stdout, "\n                    2. Dump packets");
  1452.     selection = GetYesNoOption();
  1453.     if (selection == kQuitTest)
  1454.         err = -1;
  1455.     else if (selection == kAcceptOption)
  1456.     {
  1457.             // ask whether to use the set memory limits option or not
  1458.         fprintf(stdout, "\nDo you want to set memory limits?");
  1459.         selection = GetYesNoOption();
  1460.         
  1461.         if (selection == kQuitTest)
  1462.             err = -1;
  1463.         else if (selection == kAcceptOption)
  1464.         {
  1465.             fprintf(stderr, "How much memory to grow heap ?\n");
  1466.             if (gets(mystr) != 0)
  1467.             {
  1468.                 StringToNum(c2pstr(mystr), (long*)&growAmt);
  1469.                 OTSetMemoryLimits(growAmt, 0);
  1470.                 
  1471.             }
  1472.         }
  1473.         
  1474.         if (err == kOTNoError)
  1475.         {
  1476.                 // ask whether to use the set memory limits option or not
  1477.             fprintf(stdout, "\nDo you want to just dump packets?");
  1478.             selection = GetYesNoOption();
  1479.             if (selection == kQuitTest)
  1480.                 err = -1;
  1481.             else if (selection == kAcceptOption)
  1482.             {
  1483.                 SetdumpPktsFlag(gFlags);
  1484.             }
  1485.         }
  1486.     }
  1487.  
  1488.     if (err == kOTNoError)
  1489.     {
  1490.  
  1491.             // load in the various strings
  1492.         GetIndString((unsigned char*)&gIdleStr, kServerStrID, kIdleStrID);
  1493.         GetIndString((unsigned char*)&gBusyStr, kServerStrID, kBusyStrID);
  1494.         GetIndString((unsigned char*)&gServerNBPNameStr, kServerStrID, kServerNBPStrID);
  1495.             // covert each string to c
  1496.         p2c(gIdleStr);
  1497.         p2c(gBusyStr);
  1498.         p2c(gServerNBPNameStr);
  1499.     
  1500.         // initialize the endpoint ref
  1501.         err = InitMyEndpointRef(&gEp);
  1502.     }
  1503.     
  1504.     if (err == kOTNoError)
  1505.     {
  1506. #if HANDOFF_EP
  1507.         for (i = 0; (i < kMaxHandoffEPs) && (err == kOTNoError) ; i++)
  1508.             err = InitMyEndpointRef(&gHandoffEp[i]);
  1509. #endif
  1510.     }
  1511.  
  1512.     if (err == kOTNoError)
  1513.     {
  1514.         //
  1515.         // Initialize the buffers
  1516.         //
  1517.         err = InitPAPBuffers();
  1518.     }
  1519.             
  1520.  
  1521.     if (err == kOTNoError)
  1522.     {
  1523.         //
  1524.         //  Init the server endpoint
  1525.         //
  1526.         err = ActivatePAPEndpoint(&gEp);
  1527.     }
  1528.  
  1529. #if HANDOFF_EP
  1530.     if (err == kOTNoError)
  1531.     {
  1532.         //
  1533.         //  Init the handoff endpoint
  1534.         //
  1535.         for (i = 0; (i < kMaxHandoffEPs) && (err == kOTNoError); i++)
  1536.             gHandoffEp[i].ep = OTOpenEndpoint(OTCreateConfiguration(kPAPName), (OTOpenFlags)NULL, NULL, &err);
  1537.  
  1538.         if (err != kOTNoError)
  1539.         {
  1540.             fprintf(stderr, "\n\nError opening the handoff endpoint!\n");
  1541.             fprintf(stderr, "\nBye Bye.\n");
  1542.         }
  1543.         else
  1544.         {
  1545.             for (i = 0; (i < kMaxHandoffEPs) && (err == kOTNoError); i++)
  1546.             { 
  1547.                 SetEPActiveFlag(gHandoffEp[i].flags);
  1548.                     // turn on the EOM option
  1549.                 err = DoNegotiateEOMOption(gHandoffEp[i].ep, true);
  1550.                 if (err != kOTNoError)
  1551.                 {
  1552.                     ClrEOMOnFlag(gHandoffEp[i].flags);
  1553.                     fprintf(stderr, "\n\nError setting EOM option!\n");
  1554.                 }
  1555.                 else
  1556.                 {
  1557.                     SetEOMOnFlag(gHandoffEp[i].flags);
  1558.                 }
  1559.             }
  1560.         }
  1561.     }
  1562.  
  1563.  
  1564.     if (err == kOTNoError)
  1565.     {
  1566.         for (i = 0; (i < kMaxHandoffEPs) && (err == kOTNoError); i++)
  1567.         {
  1568.             err = OTInstallNotifier(gHandoffEp[i].ep, EventHandler, (void*)&gHandoffEp[i]);
  1569.             if ( err != kOTNoError )
  1570.             {
  1571.                 fprintf(stderr, "ERROR: InstallNotifier() failed with %ld\n for handoff endpoint", err);
  1572.             }
  1573.             else
  1574.                 OTSetAsynchronous(gHandoffEp[i].ep);
  1575.         }
  1576.  
  1577.     }
  1578. #endif        // HANDOFF_EP    
  1579.         
  1580.     return err;
  1581.     
  1582. }
  1583.  
  1584.  
  1585. /*******************************************************************************
  1586. ** ReleasePAPMemory
  1587. ********************************************************************************/
  1588.  
  1589. void ReleasePAPMemory(void)
  1590. {
  1591.     PacketPtr    packetPtr;
  1592.     OTLIFO        *otLIFOPtr;
  1593.     short        i;
  1594.     
  1595.     if (gTempPackPtr)
  1596.         OTFreeMem(gTempPackPtr);
  1597.         
  1598.         // first release the freeQ memory
  1599.     otLIFOPtr = gFreeQ;
  1600.     packetPtr = (PacketPtr)OTLIFODequeue(otLIFOPtr);
  1601.     
  1602.     while (packetPtr != nil)    // while the queue is not empty
  1603.     {
  1604.                 // free the packetbuffer
  1605.         OTFreeMem(packetPtr);
  1606.         packetPtr = (PacketPtr)OTLIFODequeue(otLIFOPtr);            
  1607.     }
  1608.  
  1609. #if HANDOFF_EP
  1610.     for (i = 0; i < kMaxHandoffEPs; i++)        // 
  1611.     {
  1612.         otLIFOPtr = gHandoffEp[i].usedQ;
  1613. #else
  1614.         otLIFOPtr = gEp.usedQ;
  1615. #endif
  1616.         if (otLIFOPtr->fHead)
  1617.             fprintf(stderr, "Am releasing buffer from the used queue.\n\n");
  1618.         
  1619.         packetPtr = (PacketPtr)OTLIFODequeue(otLIFOPtr);
  1620.         
  1621.         while (packetPtr != nil)    // while the queue is not empty
  1622.         {
  1623.                     // free the packetbuffer
  1624.             OTFreeMem(packetPtr);
  1625.             packetPtr = (PacketPtr)OTLIFODequeue(otLIFOPtr);            
  1626.         }
  1627.     
  1628. #if HANDOFF_EP
  1629.     }
  1630. #endif
  1631.     
  1632.     OTFreeMem(gFreeQ);
  1633.     
  1634. }
  1635.  
  1636. /*******************************************************************************
  1637. ** Close endpoints, and OpenTransport 
  1638. ********************************************************************************/
  1639.  
  1640. void ClosePAPServerStuff(void)
  1641. {
  1642.  
  1643.     short i;
  1644.  
  1645.     if (gEp.ep)
  1646.         OTSetSynchronous(gEp.ep);
  1647.  
  1648.     // note that we don't check whether the endpoint is still connected.  Since
  1649.     // we are leving anyway, let the UnBind call clear things up.  If we did do a
  1650.     // disconnect here, we would meed to also implement a timer mechanism. so that
  1651.     // we didn't get hung awaiting a DisconnectComplete event that might not come.
  1652.     
  1653. #if HANDOFF_EP
  1654.     for (i = 0; i < kMaxHandoffEPs; i++)
  1655.     {
  1656.  
  1657.         if (gHandoffEp[i].ep)
  1658.             OTSetSynchronous(gHandoffEp[i].ep);
  1659.         
  1660.         if (TstEPBoundFlag(gHandoffEp[i].flags))    // is the handoff endpoint bound
  1661.             OTUnbind(gHandoffEp[i].ep);            // unbind the endpoint
  1662.         
  1663.         if (gHandoffEp[i].usedQ != nil)            // free allocated memory with usedQ.
  1664.             OTFreeMem(gHandoffEp[i].usedQ);
  1665.             
  1666.         if (TstEPActiveFlag(gHandoffEp[i].flags))    // is the handoff endpoint open
  1667.             OTCloseProvider(gHandoffEp[i].ep);        // close the endpoint
  1668.             
  1669.     }
  1670.  
  1671. #endif
  1672.             
  1673.     if (TstEPBoundFlag(gEp.flags))            // is the server endpoint bound
  1674.         OTUnbind(gEp.ep);                    // unbind the endpoint
  1675.         
  1676.     if (gEp.usedQ != nil)                    // free allocated memory with usedQ.
  1677.         OTFreeMem(gEp.usedQ);
  1678.         
  1679.     if (TstEPActiveFlag(gEp.flags))            // is the server endpoint open
  1680.         OTCloseProvider(gEp.ep);            // close the endpoint
  1681.         
  1682.     // ReleasePAPMemory();                        // release memory before OT goes away
  1683.                                             // not really necessary since we are just going to 
  1684.                                             // exit after this.
  1685.  
  1686.     if (gFreeQ != nil)
  1687.         OTFreeMem(gFreeQ);
  1688.     
  1689.     if (TstOTActiveFlag(gFlags))            // is OpenTransport active
  1690.         CloseOpenTransport();                // shutdown OT services
  1691.     
  1692. }
  1693.  
  1694. //
  1695. //    NetInit:
  1696. //
  1697. //    This routine does various networking related startup tasks:
  1698. //
  1699. //    (1) it does InitOpenTransport
  1700. //    (2) it records the OT version for us.
  1701. //
  1702. //  result - true - OpenTransport init'd and the correct version is present
  1703. //             false - problem opening OT or OT version not correct
  1704. //
  1705. static Boolean NetInit()
  1706. {
  1707.     OSStatus err;
  1708.  
  1709.     err = Gestalt(gOTVersionSelector, (long*) &gOTVersion);
  1710.     if (err || (gOTVersion < kOTVersion111))
  1711.     {
  1712.         fprintf(stderr, "Please install Open Transport 1.1.1 or later");
  1713.         return false;
  1714.     }
  1715.     
  1716.     err = InitOpenTransport();
  1717.     if (err)
  1718.     {
  1719.         fprintf(stderr, "NetInit: InitOpenTransport error %d", err);
  1720.         return false;
  1721.     }
  1722.     return true;
  1723. }
  1724.  
  1725.  
  1726. /*******************************************************************************
  1727. ** main function
  1728. ********************************************************************************/
  1729.  
  1730. main(void) 
  1731. {
  1732.     OSStatus    osstatus;
  1733.     
  1734.     InitGraf(&qd.thePort);                    // initialize quickdraw so we can use regions
  1735.  
  1736.     fprintf(stderr, "PAPServerSample showing implementation of PAP server.\n\n");
  1737.  
  1738.     // init the flags variable
  1739.     gFlags = 0;
  1740.  
  1741. #if DO_DEBUG_LOG
  1742.     DebugStr("\p doing log;dx;log papserver;g");
  1743. #endif
  1744.  
  1745.     if (NetInit() == false)
  1746.         return 0;
  1747.     
  1748.     //
  1749.     // Initialize the OT and the global Endpoints
  1750.     //
  1751.     osstatus = InitPAPServerStuff();
  1752.     
  1753.     if (osstatus == kOTNoError)
  1754.     {
  1755.     
  1756.  
  1757.         //
  1758.         // Run the PAPServer code
  1759.         //
  1760.         DoServer();
  1761.         //
  1762.     }
  1763.     
  1764.     //
  1765.     // Close things down
  1766.     //
  1767.     ClosePAPServerStuff();
  1768.     
  1769.     CheckFileToClose();
  1770.     
  1771.     fprintf(stderr, "\n\nDone\n");
  1772.         
  1773.     return 0;
  1774. }
  1775.  
  1776. void DoValueBreak(long value, const char* message)
  1777. {
  1778.     static short    sDoErrorBreak = 0;
  1779.  
  1780.     {
  1781.         Str255    s,
  1782.                 n = "\p";
  1783.  
  1784.         s[0] = strlen(message);
  1785.         BlockMoveData(message,&s[1],s[0]);
  1786.         if (value < 0)
  1787.         {
  1788.             s[0] += 1;
  1789.             s[s[0]] = '-';
  1790.             value = -value;
  1791.         }
  1792.         while (value)
  1793.         {
  1794.             if (n[0])
  1795.                 BlockMoveData(&n[1],&n[2],n[0]);
  1796.             n[0]++;
  1797.             n[1] = 48 + (value % 10);
  1798.             value /= 10;
  1799.         }
  1800.         BlockMoveData(&n[1],&s[s[0]+1],n[0]);
  1801.         s[0] += n[0];
  1802.  
  1803.         sDoErrorBreak++;
  1804.         {
  1805.             short    cnt = sDoErrorBreak;
  1806.  
  1807.             s[0]++;
  1808.             s[s[0]] = ',';
  1809.             s[0]++;
  1810.             s[s[0]] = ' ';
  1811.             n[0] = 0;
  1812.             while (cnt)
  1813.             {
  1814.                 if (n[0])
  1815.                     BlockMoveData(&n[1],&n[2],n[0]);
  1816.                 n[0]++;
  1817.                 n[1] = 48 + (cnt % 10);
  1818.                 cnt /= 10;
  1819.             }
  1820.             BlockMoveData(&n[1],&s[s[0]+1],n[0]);
  1821.             s[0] += n[0];
  1822.         }
  1823.         DebugStr(s);
  1824.     }
  1825. }
  1826.  
  1827.  
  1828. OSStatus BeginSetServerStatusOption(EndpointRef ep, UInt16 whichStr)
  1829. {
  1830.     OSStatus    err;
  1831.     unsigned char    *in = "\p._in";
  1832.     
  1833.     if (whichStr == kSetIdleStr)
  1834.     {
  1835.         err = DoSetServerStatusOption(ep, (char*)&gIdleStr);
  1836.     }
  1837.     else
  1838.     {
  1839.         err = DoSetServerStatusOption(ep, (char*)&gBusyStr);
  1840.     }
  1841.     
  1842.     if (err != kOTNoError)
  1843.     {
  1844.         fprintf(stderr, "ServerStatus option call failed\n");
  1845.         fprintf(stderr, "however, this will not keep the server from running\n");
  1846.         err = kOTNoError;
  1847.     }
  1848.     
  1849.     return err;
  1850. }
  1851.  
  1852.  
  1853. UInt32 GetYesNoOption(void)
  1854. {
  1855.     UInt32    result;
  1856.     char    selection[32];
  1857.     Boolean    done;
  1858.     
  1859.     fprintf(stdout, "\n    Enter Y - To accept option");
  1860.     fprintf(stdout, "\n    Enter N - To decline option");
  1861.     fprintf(stdout, "\n    Enter Q - To quit");
  1862.     fprintf(stdout, "\nYour selection -> ");
  1863.     fflush(stdout);
  1864.     done = false;
  1865.  
  1866.     do
  1867.     {
  1868.         if (gets(selection) != 0)
  1869.         {
  1870.             switch (selection[0])
  1871.             {
  1872.                 case 'y':
  1873.                 case 'Y':
  1874.                     result = kAcceptOption;
  1875.                     done = true;
  1876.                     break;
  1877.                 
  1878.                 case 'n':
  1879.                 case 'N':
  1880.                     result = kDeclineOption;
  1881.                     done = true;
  1882.                     break;
  1883.  
  1884.                 case 'q':
  1885.                 case 'Q':
  1886.                     result = kQuitTest;
  1887.                     done = true;
  1888.                     break;
  1889.                     
  1890.                 default:
  1891.                     fprintf(stdout, "\nInvalid entry - %c, try again -> ", selection);
  1892.                     fflush(stdout);
  1893.                     break;
  1894.  
  1895.             }
  1896.         }
  1897.     } while (!done);
  1898.     
  1899.     fflush (stdout);
  1900.     return result;
  1901. }
  1902.  
  1903.  
  1904. #if HANDOFF_EP
  1905. /*
  1906.     Iterate through the handoff endpoints and find one that doesn't have the busy flag
  1907.     set - return the pointer else return null
  1908. */
  1909. MyEndpointRef*    FindFreeHandoffEp(void)
  1910. {
  1911.     short    i;
  1912.     
  1913.     for (i = 0; i < kMaxHandoffEPs; i++)
  1914.     {
  1915.         if (TstEPBusyFlag(gHandoffEp[i].flags) == false)
  1916.             return &gHandoffEp[i];
  1917.     }
  1918.     
  1919.     return nil;
  1920. }
  1921. #endif
  1922.  
  1923. Boolean EndpointsAllBusy(void)
  1924. {
  1925.     Boolean        result = true;    
  1926. #if HANDOFF_EP
  1927.     short        i;
  1928.     
  1929.     for (i = 0; (i < kMaxHandoffEPs) && (result == true); i++)
  1930.     {
  1931.         if (TstEPBusyFlag(gHandoffEp[i].flags) == false)
  1932.             result = false;    // any endpoint not busy means that not all eps are busy
  1933.     }
  1934.  
  1935. #else
  1936.     result = TstEPBusyFlag(gEp.flags);
  1937.         
  1938. #endif
  1939.     
  1940.     return result;
  1941. }